package client.redis.impl;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

import client.redis.IRedisClient;
import core.AlertUtil;
import core.FormDialog;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisClusterCommand;
import redis.clients.jedis.JedisClusterConnectionHandler;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.ListPosition;
import redis.clients.jedis.Tuple;

/**
 * 单台redis连接客户端
 * @author self
 *
 */
public class ClusterRedisClientImpl implements IRedisClient {

	private static Logger log=LoggerFactory.getLogger(ClusterRedisClientImpl.class);

	//客户端名称
	private static final String CLIENT_NAME = "Redis客户端(集群)";

	//Jedis客户端
	private ClusterClient clusterClient;

	//连接信息
	private ConnectInfo connectInfo;

	@Override
	public String getName() {
		return CLIENT_NAME;
	}

	@Override
	public String getConnStr() {
		return connectInfo.getNodes();
	}

	@Override
	public boolean init() {
		log.info("初始化客户端组件！");
		return createConnection();
	}

	/**
	 * 创建连接
	 * @return
	 */
	protected boolean createConnection() {
		log.info("创建Socket连接！");
		TextArea nodesTextArea = new TextArea();
		TextField authTextField = new TextField();
		FormDialog newConnForm = new FormDialog("新建链接"
					,new FormDialog.Item("集群节点：", nodesTextArea)
					,new FormDialog.Item("认证：", authTextField));
		Optional<Boolean> result = newConnForm.showAndWait();
		if(result.isPresent()) {
			String nodes = nodesTextArea.getText();
			String auth = authTextField.getText();
			log.info("Socket连接参数 -> nodes:"+nodes+",auth:"+auth);
			connectInfo = new ConnectInfo(nodes);
			Set<HostAndPort> hostAndPorts = new HashSet<HostAndPort>();
			String[] hostAndPortStrList = nodes.split(",");
			for(int i=0;i<hostAndPortStrList.length;i++) {
				String hostAndPortStr = hostAndPortStrList[i].trim();
				String[] hostAndPortArray = hostAndPortStr.split(":");
				String host = hostAndPortArray[0];
				int port = Integer.valueOf(hostAndPortArray[1]);
				hostAndPorts.add(new HostAndPort(host, port));
			}
			if(auth!=null&&!"".equals(auth.trim())) {
				clusterClient = new ClusterClient(hostAndPorts,auth);
			}else {
				clusterClient = new ClusterClient(hostAndPorts);
			}
			log.info("已连接到Redis服务器。");
			return true;
		}
		return false;
	}

	@Override
	public void select(int i) {
		AlertUtil.warning("集群模式只有db0");
	}

	@Override
	public void linsert(int db,String key,ListPosition where,String pivot,String value) {
		clusterClient.linsert(key, where, pivot, value);
	}

	@Override
	public void rpush(int db, String key, String value) {
		clusterClient.rpush(key, value);
	}

	@Override
	public void lpush(int db, String key, String value) {
		clusterClient.lpush(key, value);
	}

	@Override
	public void lset(int db, String key, long index, String value) {
		clusterClient.lset(key, index, value);
	}

	@Override
	public void lpop(int db, String key) {
		clusterClient.lpop(key);
	}

	@Override
	public void rpop(int db, String key) {
		clusterClient.rpop(key);
	}

	@Override
	public void srem(int db, String key, String item) {
		clusterClient.srem(key, item);
	}

	@Override
	public void zrem(int db, String key, String item) {
		clusterClient.zrem(key, item);
	}

	@Override
	public void lrem(int db, String key, String item) {
		clusterClient.lrem(key, 0, item);
	}

	@Override
	public void hdel(int db, String key, String item) {
		clusterClient.hdel(key, item);
	}

	@Override
	public void rename(int db,String key,String newName) {
		clusterClient.rename(key,newName);
	}

	@Override
	public String type(int db, String key) {
		return clusterClient.type(key);
	}

	@Override
	public Set<String> keys(int db, String string) {
		return clusterClient.keys(string);
	}

	@Override
	public String getRedisInfo() {
		StringBuilder infoBuilder = new StringBuilder();
		Map<String, JedisPool> clusterNodes = clusterClient.getClusterNodes();
		for(String nodeName:clusterNodes.keySet()) {
			infoBuilder.append("-------------------------- "+nodeName+" -------------------------\n");
			infoBuilder.append(clusterNodes.get(nodeName).getResource().info()).append("\n\n");
		}
		return clusterNodes.toString();
	}

	@Override
	public String getDBInfo(int db) {
		Map<String,Object> dbInfo = new HashMap<String, Object>();
		dbInfo.put("db", db);
		return JSON.toJSONString(dbInfo, true);
	}

	@Override
	public Long ttl(int db, String key) {
		return clusterClient.ttl(key);
	}

	@Override
	public String get(int db, String key) {
		return clusterClient.get(key);
	}

	@Override
	public Set<String> smembers(int db, String key) {
		return clusterClient.smembers(key);
	}

	@Override
	public Set<Tuple> zrangeWithScores(int db, String key, int i, int j) {
		return clusterClient.zrangeWithScores(key,i,j);
	}

	@Override
	public List<String> lrange(int db, String key, int i, int j) {
		return clusterClient.lrange(key, i, j);
	}

	@Override
	public Map<String, String> hgetAll(int db, String key) {
		return clusterClient.hgetAll(key);
	}

	@Override
	public void set(int db, String name, String value) {
		clusterClient.set(name,value);
	}

	@Override
	public void sadd(int db, String name, String[] array) {
		clusterClient.sadd(name, array);
	}

	@Override
	public void sadd(int db, String name, String value) {
		clusterClient.sadd(name, value);
	}

	@Override
	public void zadd(int db, String name, Map<String, Double> data) {
		clusterClient.zadd(name,data);
	}

	@Override
	public void lpush(int db, String name, String[] array) {
		clusterClient.lpush(name,array);
	}

	@Override
	public void hset(int db, String name, Map<String, String> data) {
		clusterClient.hset(name,data);
	}

	@Override
	public void del(int db, String key) {
		clusterClient.del(key);
	}

	@Override
	public void expire(int db, String key, int expire) {
		clusterClient.expire(key,expire);
	}

	@Override
	public void persist(int db, String key) {
		clusterClient.persist(key);
	}

	@Override
	public void flushDB(int db) {
		AlertUtil.warning("集群模式不支持flushDB");
	}

	@Override
	public String exeCmd(String cmd) {
		if(cmd==null||"".equals(cmd.trim())) {
			return null;
		}
		log.info("执行命令："+cmd);
		String[] args = cmd.split("\\s+");
		if(args.length < 2) {
			return "\""+args[0]+"\" command not support!!!";
		}
		//命令如果没有换行符则补充一个
		if(!cmd.endsWith("\n")) {
			cmd+="\n";
		}
		//第二个参数认为是key
		String key = args[1];
		final String redisCmd = cmd;
		final StringBuilder builder = new StringBuilder();
		new JedisClusterCommand<String>(clusterClient.getConnectionHandler(), clusterClient.getMaxAttempts()) {
		  @Override
		  public String execute(Jedis jedis) {
			  	try {
					/*
					 * 将命令写入Socket
					 */
					OutputStream output = jedis.getClient().getSocket().getOutputStream();
					output.write(redisCmd.getBytes(StandardCharsets.UTF_8));

					/*
					 * 从Socket读取响应
					 */
					InputStreamReader input = new InputStreamReader(jedis.getClient().getSocket().getInputStream(),StandardCharsets.UTF_8);
					char[] chareBuf = new char[1024];
					int len;
					while((len=input.read(chareBuf))!=-1) {
						builder.append(chareBuf,0,len);
						if(len<chareBuf.length) {
							break;
						}
					}
				} catch (IOException e) {
					AlertUtil.exception(e);
				}
				return "+OK";
		  }
		}.run(key);

		String result=builder.toString();
		log.info("执行结果："+result);
		return result;
	}

	@Override
	public void close() throws IOException {
		if(clusterClient!=null) {
			clusterClient.close();
		}
	}

	private static class ClusterClient extends JedisCluster{

		public ClusterClient(Set<HostAndPort> nodes) {
			super(nodes);
		}

		public ClusterClient(Set<HostAndPort> nodes,String password) {
			super(nodes,DEFAULT_TIMEOUT,DEFAULT_TIMEOUT,DEFAULT_MAX_ATTEMPTS,password,new GenericObjectPoolConfig<>());
		}

		public int getMaxAttempts() {
			return maxAttempts;
		}

		public JedisClusterConnectionHandler getConnectionHandler() {
			return connectionHandler;
		}
	}

	private static class ConnectInfo{
		private String nodes;

		public ConnectInfo(String nodes) {
			this.nodes = nodes;
		}

		public String getNodes() {
			return nodes;
		}
	}
}
